package com.example.base.vm

import android.util.Log
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.lifecycle.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import com.example.base.BaseApp
import com.example.base.BaseApp.Companion.context
import com.example.base.entity.Rest
import com.example.base.entity.RestNoData
import com.example.base.ext.LoadingView
import com.example.base.repository.BaseRepository
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException

/**
 * ViewModel 的基类
 * @param R 跟 ViewModel 绑定的 Repository
 */
@Suppress("SameParameterValue")
abstract class BaseViewModel<R : BaseRepository>(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    protected val repository: R by lazy {
        createRepository()
    }

    /**  创建与自己相绑定的 Repository  **/
    abstract fun createRepository(): R

    /**
     * 设置一个值到 LiveData
     * @param key 设置该 LiveData 的 Key
     * @param value LiveData 的值
     */
    protected fun <T> setData(key: String, value: T) = savedStateHandle.set(key, value)

    /**
     * 从 LiveData 中取出一个值
     * @param key 目标 LiveData 的 Key
     * @param defaultValue 不存在 LiveData 值时的默认值
     */
    protected fun <T> getData(key: String, defaultValue: T): MutableLiveData<T> =
        savedStateHandle.getLiveData(key, defaultValue)

    /**
     * 从资源文件中获取 String
     * @param resId 资源 id
     */
    protected fun getString(@StringRes resId: Int) = BaseApp.context.getString(resId)

    /**
     * ViewModel 内部启动协程处理繁重任务
     * @param loadingView 加载视图的 View, 默认为 null, 则不进行视图渲染
     * @param backgroundWork 实际进行的繁重任务处理内容回调
     */
    inner class Launch(
        private val loadingView: LoadingView? = null,
        private val backgroundWork: suspend Launch.() -> Unit
    ) {


        /**  执行任务  **/
        fun complete() {
            viewModelScope.launch {
                try {
                    loadingView?.showProgress()
                    backgroundWork()
                } catch (t: Throwable) {
                    // 做多异常处理, 处理一些常见的异常, 让提示更加友好
                    processorException(t)
                    cancel()
                } finally {
                    loadingView?.hideProgress()
                }
            }
        }
        /**  发生错误时的回调处理, 默认为啥都不处理  **/
        var onError: (msg: String) -> Unit = {}
        /**  异常处理  **/
        private fun processorException(t: Throwable) {
            when (t) {
                is UnknownHostException->{
                    onError("网络不可用,请稍后重试！")

                }
                is SocketTimeoutException -> {
                    Log.e("SocketTimeoutException", "连接服务器超时!")
                    onError("您当前的网络较慢, 请稍后重试!")
                }
                is ConnectException -> { //连接超时等
                    onError("网络连接超时，请检查您的网络状态！")
                }
                is HttpException -> {

                    processorHttpException(t)

                }


                is CancellationException -> Log.e("CancellationException", "任务被取消!")
                else -> onError(t.message ?: "")
            }
            // 请求服务器超时时 message 为空
            Log.e("ViewModel", "Catch Exception!", t)
        }

        /**  处理 Http 异常  **/
        private fun processorHttpException(e: HttpException) {
            when (e.code()) {
                // 401 时表示 token 失效, 进行重新登陆 pass 掉
                401 -> Log.e("HttpException", "401 Token 失效")
                // 404 接口地址未定义
                404 -> {
                    Log.e("HttpException", "404 ${e.response()}")
                    onError("服务器发生一些错误, 请联系开发人员进行解决! 错误代号: 404")
                }
                500 -> {
                    Log.e("HttpException", "500 ${e.response()}")
                    onError("服务器发生一些问题, 请联系开发人员进行解决! 错误代号: 500")
                }
            }
        }
    }

}

/**  必然返回非空的 value  **/
fun <T> LiveData<T>.requireValue(): T = this.value!!

/**
 * ViewModel 繁重任务成功时回调接口的封装
 * @param Data 返回的数据类泛型
 */
typealias OnSuccess<Data> = (data: Rest<Data>) -> Unit
/**
 * ViewModel 繁重任务成功时回调接口的封装
 * @param Data 返回的数据类泛型
 */
typealias OnSuccessNoData<Data> = (data: RestNoData<Data>) -> Unit

/**  ViewModel 繁重任务发生错误时回调接口的封装  **/
typealias OnError = (errorMsg: String) -> Unit

/**
 * ViewModel 接口请求错误事件的回调
 * @param onError 发生错误时的回调处理内容
 */
infix fun <R : BaseRepository> BaseViewModel<R>.Launch.onError(onError: OnError) {
    this.onError = onError
    complete()
}

/**
 * LiveData 监听值的变化
 * @param owner LifecycleOwner
 * @param observer 变化监听事件
 */
inline fun <T> LiveData<T>.observe(
    owner: LifecycleOwner,
    crossinline observer: Observer<T>
) = this.observe(owner, Observer {
    observer(it)
})

typealias Observer<T> = (value: T) -> Unit